﻿#define _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"
//定义光标的位置
void SetPos(int x,int y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = NULL;
	houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置
	COORD pos = { x,y };
	SetConsoleCursorPosition(houtput, pos);

}


//打印环境界面和功能介绍
void WelcomeToGame()
{
    SetPos(40, 14);//定义光标的位置
    printf("欢迎来到贪吃蛇小游戏\n"); 
    SetPos(45, 20); 
    system("pause");//暂停
    system("cls");

    SetPos(25, 14);
    wprintf(L"用 ↑ ↓ ← → 来控制蛇的移动，按F3加速，F4减速 \n");
    SetPos(25, 15);
    wprintf(L"加速能够得到更高的分数\n");
    SetPos(45, 20);
    system("pause");
    system("cls");


}

//绘制地图 方格（29个（行）* 27个（列）） （27行 58列的地图）
void CreateMap()
{
    //上
    for (int i = 0; i < 29; i++)
    {
        wprintf(L"%lc",WALL);
    }
    //下
    SetPos(0, 26);
    for (int i = 0; i < 29; i++)
    {
        wprintf(L"%lc", WALL);
    }
    //左
    for (int i = 1; i <= 25; i++)
    {
        SetPos(0, i);
        wprintf(L"%lc", WALL);

    }
    //右
    for (int i = 1; i <= 25; i++)
    {
        SetPos(56,i);
        wprintf(L"%lc", WALL);

    }
     
}

//初始化蛇
void InitSnake(pSnake ps)
{
    int i = 0;
    pSnakeNode cur = NULL;

    for (int i = 0; i < 5; i++)
    {
        cur = (pSnakeNode)malloc(sizeof(SnakeNode));
        if (cur == NULL)
        {
            perror("malloc");
            return;

        }
        cur->next = NULL;
        cur->x = POS_X + 2 * i;
        cur->y = POS_Y;

        //头插法插入链表
        if (ps->_pSnake == NULL)//空链表
        {
            ps->_pSnake = cur;
        }
        else//非空链表
        {
            cur->next = ps->_pSnake;
            ps->_pSnake = cur;
        }
    }
    cur = ps->_pSnake;//指向蛇头的指针
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc",BODY);
        cur = cur->next;
    }
    //getchar();

    //设置蛇的属性
    ps->_dir = RIGHT;//默认向右
    ps->_socre = 0;//分数为0
    ps->_food_weight = 10;//一个食物的分数
    ps->_sleep_time = 200;//速度
    ps->_status = OK;

    //getchar();
}


//创建食物
void CreateFood(pSnake ps)
{
    int x = 0;
    int y = 0;
again:
    //x是2的倍数
    do
    {
        x = rand() % 53 + 2;//2-54
        y = rand() % 25 + 1;//1-25
    } while (x % 2 != 0);

    //x和y的坐标不能和蛇的身体坐标冲突

    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        if (x == cur->x && y == cur->y)
        {
            goto again;
        }
        cur = cur->next;       
    }

    //创建食物的节点
    pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (pFood == NULL)
    {
        perror("malloc");
        return;
    }
    pFood->x = x;
    pFood->y = y;
    pFood->next = NULL;

    SetPos(x, y);//坐标定位到食物的位置
    wprintf(L"%lc",FOOD);

    ps->_pFood = pFood;//将食物的信息储存到贪吃蛇的结构体中

    //getchar();
}


//游戏的初始化
void GameStrart(pSnake ps)
{
    //0.先设置窗口的大小，在光标隐藏
    system("mode con cols=100 lines=30");
    system("title 贪吃蛇");



    //0.光标隐藏
    HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
    //隐藏光标操作
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(houtput, &CursorInfo);//获取控制台光标信息
    CursorInfo.bVisible = false; //隐藏控制台光标
    SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态
    


     
    //1.打印环境界面和功能介绍
    WelcomeToGame();
    
    //2.绘制地图
    CreateMap();

    //3. 初始化蛇
    InitSnake(ps);

    //4.创建食物
    CreateFood(ps);

     
}

//打印第三个界面右下角的信息
void PrintHelpInfo()
{
    //打印提⽰信息
    SetPos(64, 15);
    printf("不能穿墙，不能咬到自己\n");
    SetPos(64, 16);
    wprintf(L"%ls",L"用 ↑ ↓ ← → 来控制蛇的移动");
    SetPos(64, 17);
    printf("F3 为加速，F4 为减速\n");
    SetPos(64, 18);
    printf("ESC ：退出游戏.space：暂停游戏.");
    SetPos(64, 20);
    printf("比特就业课@版权");
}

//判断下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{
    if (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y)
    {
        return 1;

    }
    else
    {
        return 0;
    }
}


//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{
     //头插法
    ps->_pFood->next = ps->_pSnake;
    ps->_pSnake = ps->_pFood;

    //释放下一个位置的节点
    free(pn);
    pn = NULL;

    pSnakeNode cur = ps->_pSnake;
    //打印蛇
    while (cur)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;

    }
    ps->_socre += ps->_food_weight;


    //重新创建食物
    CreateFood(ps);
}


//下一个坐标不是食物
void NoFood(pSnakeNode pn,pSnake ps)
{
    //头插法
    pn->next = ps->_pSnake;
    ps->_pSnake = pn;

    pSnakeNode cur = ps->_pSnake;
    while (cur->next->next)
    {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc",BODY);
        cur = cur->next;
    }

    //把最后一个节点打印成空格
    SetPos(cur->next->x, cur->next->y);
    printf("  ");

    //释放最后一个节点
    free(cur->next);

    //把倒数第二个节点的位置置为NULL
    cur->next = NULL;
}


//检测蛇是否撞墙
void KillByWall(pSnake ps)
{
    if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 ||
        ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
    {
        ps->_status = KILL_BY_WALL;
    }
}



//检测蛇是否咬到蛇身
void KillBySelf(pSnake ps)
{
    pSnakeNode cur = ps->_pSnake->next;
    while (cur)
    {
        if ((ps->_pSnake->x == cur->x) && (ps->_pSnake->y == cur->y))
        {
            ps->_status = KILL_BY_SELF;

        }
        cur = cur->next;
    }
}

//蛇的移动
void SnakeMove(pSnake ps)
{
     //创建一个节点，表示蛇即将到下一个节点
    pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (pNextNode == NULL)
    {
        perror(malloc);
        return;
    }

    switch (ps->_dir)
    {
    case UP:
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y - 1;
        break;
    case DOWN:
        pNextNode->x = ps->_pSnake->x;
        pNextNode->y = ps->_pSnake->y + 1;
        break;
    case LEFT:
        pNextNode->x = ps->_pSnake->x - 2;
        pNextNode->y = ps->_pSnake->y;
        break;
    case RIGHT:
        pNextNode->x = ps->_pSnake->x + 2 ;
        pNextNode->y = ps->_pSnake->y ;
        break;
    }


    //判断下一个坐标是否是食物
    if (NextIsFood(pNextNode, ps))//下一个是食物
    {
        EatFood(pNextNode,ps);
    }
    else//下一个不是食物
    {
        NoFood(pNextNode,ps);
    }


    //检测蛇是否撞墙 
    KillByWall(ps);

    //检测蛇是否咬到蛇身
    KillBySelf(ps);
}


//定义一个宏，检测数字键
#define KEY_PRESS(VK)  ( (GetAsyncKeyState(VK) & 1) ? 1 : 0 )


//暂停
void pause()
{
    while (1)
    {
       
       Sleep(200);
       if (KEY_PRESS(VK_SPACE))
       {
           break;
       }
    }
}




//游戏运行的逻辑
void GameRun(pSnake ps)
{
    //打印帮助信息
    PrintHelpInfo();
    do
    {
        SetPos(64, 10);
        printf("得分：%d\n", ps->_socre);
        SetPos(64, 11);
        printf("当前的食物分数：%2d\n", ps->_food_weight);


        if (KEY_PRESS(VK_UP) && ps->_dir != DOWN)//向上
        {
            ps->_dir = UP;
        }

        else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP)//向下
        {
            ps->_dir = DOWN;
        }

        else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT)//向左
        {
            ps->_dir = LEFT;
        }

        else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT)//向右
        {
            ps->_dir = RIGHT;
        }

        else if (KEY_PRESS(VK_SPACE))//暂停
        {
            pause();
        }

        else if (KEY_PRESS(VK_ESCAPE))//正常结束游戏
        {
            ps->_status = END_NORMAL;
            break;
        }

        else if (KEY_PRESS(VK_F3))//加速
        {
            if (ps->_sleep_time >= 80)
            {
                ps->_sleep_time -= 30;
                ps->_food_weight += 2;//⼀个⻝物分数最⾼是20分
            }
        }

        else if (KEY_PRESS(VK_F4))//减速
        {
            if (ps->_sleep_time < 320)
            {
                ps->_sleep_time += 30;
               
                ps->_food_weight -= 2;//⼀个⻝物分数最低是2分
            }
        }


        //蛇每次⼀定之间要休眠的时间，时间短，蛇移动速度就快
        Sleep(ps->_sleep_time);
        SnakeMove(ps);//蛇的移动

    } while (ps->_status == OK);
}



void GameEnd(pSnake ps)
{ 
    SetPos(20,12);
    switch (ps->_status)
    {
    case END_NORMAL:
        printf("您主动退出了游戏\n");
        break;
    case KILL_BY_SELF:
        printf("您撞上自己了 ,游戏结束!\n");
        break;
    case KILL_BY_WALL:
        printf("您撞墙了,游戏结束!\n");
        break;
     
    }


    //释放蛇身的链表
    pSnakeNode cur = ps->_pSnake;
    while (cur)
    {
        pSnakeNode del = cur;
        cur = cur->next;
        free(del);
    }
}